#include "DIVA_KeyFrame.h"
#include "Command_Agent.h"
#include "MappingEngine.h"
#include "xMappingTree.h"
#include "xNodeType_composite.h"
#include "xNodeType_WOP.h"
#include "xNodeType_VM.h"
#include "LyInvokedObj.h"
#include "WOPInvokedObj.h"
#include "InterpolationCenter.h"
#include "LyvmLayoutLibrary.h"
#include "Basic_Dynamic_lyvm.h"
#include "RegularPointer_new_lyvm.h"
#include "Layout_Assistant.h"
#include "ColorEvent.h"
#include "PositionEvent.h"
#include "ReferenceVM.h"
#include "dummyVM.h"
#include "xMappingNode.h"

#include <algorithm>

bool getMax(const pair<DIVA_VM*,VMStatus*> a,const pair<DIVA_VM*,VMStatus*> b){
	if((a.second->getPos().y) < (b.second->getPos().y)){
		return true;
	}
	return false;
}
DIVA_KeyFrame::DIVA_KeyFrame(){
	auto xmt = MappingEngine::getInstance()->getAllxMappingTree();
	int lineNumber = Command_Agent::getInstance()->getLineNumber();
	std::vector<DIVA_VM*> vms;
	std::vector<DIVA_VM*> exist_vms;
	// FIXME not only one root
#if 0
	for(it = xmt.begin() ; it!= xmt.end() ; ++it){
		if((*it).get_root()->getType() == "composite"){
			DIVA_VM* vm = ((xNodeType_composite*)(*it).get_root()->getNodeTypePtr())->getxMappingTree()->getRootVM();
			if(!checkVMisExisted(vm) && vm->get_visible()){
				_vms_status_table[vm] = NULL;
				vms.push_back(vm);
			}
		}
		else if((*it).get_root()->getType() == "vm"){
			DIVA_VM* vm = ((xNodeType_VM*)(*it).get_root()->getNodeTypePtr())->getVM();
			if(vm->getVMType() == "reference"){
				if(vm->get_visible()){
					exist_vms.push_back(vm);
				}
			}
		}
		std::vector<xMappingNode*> xmn = (*it).get_input_terminals();
		std::vector<xMappingNode*>::iterator itr;
		bool isProxyWOP = false;
		for(itr = xmn.begin(); itr != xmn.end() ; ++itr){
			if((*itr)->getType() == "wop"){
				WOP_Entry* en = ((xNodeType_WOP*)(*itr)->getNodeTypePtr())->getWOP();
				if(!en->getScope()){ 
					continue;				
				}
				if(en->getTypeStr() == "int" || en->getTypeStr() == "double" || en->getTypeStr() == "float"){
					WOPInvokedObj* newObj = new WOPInvokedObj(lineNumber,en);
					this->_all_wops.push_back(newObj);
					_proxy_wop_xmt_table[newObj] = (*it);
				}
			}
		}
	}
#endif
	//check if there are some VM invisible
	//exploreVM(exist_vms);
	setupVMStatusTable(vms);	
}

bool DIVA_KeyFrame::checkVMisExisted(DIVA_VM* target){
	std::vector<LyInvokedObj*>::iterator it;
	for(it = this->_all_ly_objs.begin() ; it != this->_all_ly_objs.end() ; ++it){
		if((*it)->getVM() == target){
			return true;
		}
		else if((*it)->getVM()->isSubComponent(target)){
			return true;
		}
	}
	return false;
}

void DIVA_KeyFrame::render(float timeSinceLastFrame){
	/*wop key frame object*/	
	vector<WOPInvokedObj*>::iterator itw;
	for(itw = _all_wops.begin(); itw != _all_wops.end(); ++itw){
		(*itw)->render(timeSinceLastFrame);
	}
	triggerProxyWOPValueUpdate();
	/*layout invoked key frame object*/
	vector<LyInvokedObj*>::iterator itl;
	for(itl = _all_ly_objs.begin(); itl != _all_ly_objs.end(); ++itl){
		(*itl)->render(timeSinceLastFrame);
	}
	/*scope invoked key frame object*/
	vector<ScopeInvokedObj*>::iterator its;
	for(its = _all_scope_obj.begin() ; its != _all_scope_obj.end() ; ++its){
		(*its)->render(timeSinceLastFrame);
	}
	/*should VM can be moved in Animation mode ?*/
	if(_all_ly_objs.size() == 0){
		VMStatusTable::iterator vmst;
		for(vmst = _vms_status_table.begin(); vmst != _vms_status_table.end(); ++vmst){
			DIVA_VM* vm = (*vmst).first;
			Ogre::Vector3 pos = (*vmst).second->getPos();
			vm->setPosition(pos);
		}
	}

}

void DIVA_KeyFrame::recover(){
	/*wop key frame object*/	
	vector<WOPInvokedObj*>::iterator it;
	for(it = _all_wops.begin() ; it!= _all_wops.end() ; ++it){
		(*it)->recover();
	}
	triggerProxyWOPValueUpdate();
	recoverVM();
	//recoverScopeInvokedObject();
}

void DIVA_KeyFrame::recoverVM(){
	/*layout invoked key frame object*/
	vector<LyInvokedObj*>::iterator itl;
	for(itl = _all_ly_objs.begin() ; itl!= _all_ly_objs.end() ; ++itl){
		(*itl)->recover();
	}
	/*scope invoked key frame object*/
	vector<ScopeInvokedObj*>::iterator its;
	for(its = _all_scope_obj.begin() ; its!= _all_scope_obj.end() ; ++its){
		(*its)->recover();
	}
}

void DIVA_KeyFrame::restartVM(){
	/*layout invoked key frame object*/
	vector<LyInvokedObj*>::iterator itl;
	for(itl = _all_ly_objs.begin() ; itl!= _all_ly_objs.end() ; ++itl){
		(*itl)->restart();
	}
	/*scope invoked key frame object*/
	vector<ScopeInvokedObj*>::iterator its;
	for(its = _all_scope_obj.begin() ; its!= _all_scope_obj.end() ; ++its){
		(*its)->restart();
	}
}

VMStatus* DIVA_KeyFrame::getVMStatus(DIVA_VM* vm){
	VMStatusTable::iterator it;
	it = _vms_status_table.find(vm);
	if(it != _vms_status_table.end()){
		return (*it).second;
	}
	return NULL;
}

void DIVA_KeyFrame::addNewScopeInvokedObj(ScopeInvokedObj* sio){
	VMStatus* status;
	Ogre::Vector4 preColor;
	if(sio->getVM()->get_visible()){
		status = _vms_status_table[sio->getVM()];
		preColor = status->getColor();
	}
	else{
		Ogre::Real r = *(sio->getVM()->get_vm_r());
		Ogre::Real g = *(sio->getVM()->get_vm_r());
		Ogre::Real b = *(sio->getVM()->get_vm_r());
		Ogre::Real t = *(sio->getVM()->get_vm_r());
		preColor = Ogre::Vector4(r,g,b,t);
	}
	Ogre::Vector4 currentColor = Ogre::Vector4(1.0f,1.0f,1.0f,1.0f);//白色
	sio->setChangeEvent(new ColorEvent(currentColor,preColor));
	this->_all_scope_obj.push_back(sio);
}

void DIVA_KeyFrame::addNewLyInvokedObj(LyInvokedObj* lyio){
	this->_all_ly_objs.push_back(lyio);
}

void DIVA_KeyFrame::exploreVM(){
	//map<VMStatus*,DIVA_VM*> exploreTable;
	// now we want to compute the root VM (topVM) and then explore
	// this part could be ignored if the VM is explored automatically before snapshot
	std::vector<DIVA_VM*> exploreVMs;
	VMStatusTable::iterator it;
	for(it = _vms_status_table.begin() ; it != _vms_status_table.end() ; ++it){
		if((*it).first->get_visible()){
			exploreVMs.push_back((*it).first);
		}
	}
	if(exploreVMs.size() == 0) return;
	std::vector<DIVA_VM*>::iterator itr = exploreVMs.begin();
	DIVA_VM* theTopVM = (*itr);
	for(itr = exploreVMs.begin() ; itr != exploreVMs.end() ; ++itr){
		if(theTopVM->getPosition().y < (*itr)->getPosition().y){
			theTopVM = (*itr);
		}
	}
	Layout_Assistant::getInstance()->d_expand(theTopVM,0,0);
	/*for(it = _vms_status_table.begin() ; it != _vms_status_table.end() ; ++it){
		exploreTable.insert(make_pair((*it).second,(*it).first);
	}
	map<VMStatus*,DIVA_VM*>::iterator it;
	*/
}

void DIVA_KeyFrame::setupVMStatusTable(vector<DIVA_VM*> vms){
	//得到目前有開啟的Dynamic layout
	Basic_Dynamic_lyvm* lyvm = LyvmLayoutLibrary::getInstance()->getCurrentEnabledDynamicLy();
	positionmap pm;
	if(vms.size() != 0 && lyvm != NULL){
		lyvm->layout(vms);
		pm = lyvm->getPositionRecord();
	}
	if(pm.size() == 0){
		//使用regularpointer_new_lyvm
		RegularPointer_new_lyvm r;
		std::vector<DIVA_VM*>::iterator vmit;
		for(vmit = vms.begin() ; vmit != vms.end() ; ++vmit){
			r.doNewLayout((*vmit));
		}
		pm = r.getVMPositionRecord();
	}
	else{
		//有開啟dynamic layout
	}
	VMStatusTable::iterator vmst;
   // _vms_status_table save the current position and color
	for(vmst = _vms_status_table.begin(); vmst != _vms_status_table.end(); ++vmst){
		DIVA_VM* current = (*vmst).first;
		positionmap::iterator pmit;
		pmit = pm.find(current);
		assert(pmit != pm.end());
		if(!current->get_visible()){
			current->VMSetVisible(true);
		}
		Ogre::Vector3 pos = (*pmit).second;
		Ogre::Real r = *(current->get_vm_r());
		Ogre::Real g = *(current->get_vm_g());
		Ogre::Real b = *(current->get_vm_b());
		Ogre::Real t = *(current->get_vm_transparency());
		Ogre::Vector4 color = Ogre::Vector4(r,g,b,t);
		(*vmst).second = new VMStatus(pos,color);
	}
}

void DIVA_KeyFrame::triggerProxyWOPValueUpdate(){
	ProxyWOPTable::iterator it;
	for(it = _proxy_wop_xmt_table.begin() ; it!= _proxy_wop_xmt_table.end() ; ++it){
		vector<xMappingNode*> xmpn = (*it).second.get_nodes();
		vector<xMappingNode*>::iterator itr;
		for(itr = xmpn.begin() ; itr != xmpn.end() ; ++itr){
			if((*itr)->getNodeTypePtr()->getType() == "vm"){
				DIVA_VM* vm = ((xNodeType_VM*)(*itr)->getNodeTypePtr())->getVM();
				if(vm->getVMType() != "reference"){
					vm->VMValueUpdated();
				}
			}
		}
	}
}

/*void DIVA_KeyFrame::pruneWOPObjisOutOfScope(std::vector<WOPInvokedObj*> pre){

	vector<WOPInvokedObj*>::iterator it1,it2;
	ProxyWOPTable::iterator it;
	for(it1 = _all_wops.begin() ; it1 != _all_wops.end() ;){
	bool change = false;
		for(it2 = pre.begin() ; it2 != pre.end() ; ++it2){
			if((*it1)->getEntry() == (*it2)->getEntry()){
				it = _proxy_wop_xmt_table.find((*it1));
				_proxy_wop_xmt_table.erase(it);
				_all_wops.erase(it1);				
				change = true;
				break;
			}
		}
		if(change){
			it1 = _all_wops.begin();
		}
		else{
			break;
		}
	}
}*/
